/// 
/// MaDAson - Toolkit for Model Driven Software Development
/// 
/// Copyright [2006] Marcus Lankenau
/// 
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///  
///     http://www.apache.org/licenses/LICENSE-2.0
///  
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// 

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Madason.MetaModel.Interfaces;
using Madason.MetaModel;
using System.Reflection;

using System.Threading;
using System.Diagnostics;
using Madason.MetaModel.Validation;

namespace Madason.GenUtils
{
	/// <summary>
	/// Baseclass for generatorss
	/// </summary>
	public class GeneratorWorkflow
	{
		#region private memeber

		
		private List<ParameterDefinition> paremeterDefinitionList = new List<ParameterDefinition>();
		private Dictionary<string, string> arguments = new Dictionary<string, string>();
		private Madason.MetaModel.Model model = null;

		#endregion

		#region properties

		/// <summary>
		/// the current model
		/// </summary>
		public Madason.MetaModel.Model Model
		{
			get { return model; }
			set { model = value; }
		}

		public Dictionary<string, string> Arguments
		{
			get { return arguments; }
		}


		#endregion


		/// <summary>
		/// public ctor
		/// </summary>
		/// <param name="args"></param>
		public GeneratorWorkflow(string[] args)
		{
			foreach (string arg in args)
			{
				string[] pair = arg.Split("=".ToCharArray());
				if (pair.Length == 2)
					arguments[pair[0]] = pair[1];
				else
					arguments[arg] = "";
			}

			// adding default paremetrs
			AddParameterDefinition("xmi", ParameterType.File, "path to model file (xmi or native)", true);
			AddParameterDefinition("import", ParameterType.Selection, "path to xmi file", true, "StarUML", new String[] { "XMI11", "StarUML" });			
		}

		public string GetArg(string key)
		{
			return arguments[key];
		}

		/// <summary>
		/// Check CommandLine
		/// </summary>
		public void CheckCommandLine()
		{
			if (arguments.Count == 0 || arguments.ContainsKey("gui"))
				ShowParameterDialog();

			foreach (ParameterDefinition def in paremeterDefinitionList)
			{
				if (def.Required && !arguments.ContainsKey(def.Name))
				{
					DisplayCommandLineHelp();
														
					Console.WriteLine("missing required argument " + def.Name);
					Process.GetCurrentProcess().Kill();
				}
				if (arguments.ContainsKey(def.Name))
				{
					string value = arguments[def.Name];
					if (def.ParameterType == ParameterType.Bool)
					{
						if (value.ToLower() != "yes" && value.ToLower() != "no")
						{
							Console.WriteLine("argument " + def.Name + " must be either yes or no");
							Process.GetCurrentProcess().Kill();
						}
					}
				}
			}

			// help to attach debugger to generator process
			if (arguments.ContainsKey("wait"))
			{
				Console.WriteLine("Press any key...");
				Console.ReadKey();				
			}

			if (arguments.ContainsKey("gui"))
				ShowParameterDialog();
		}

		public void ShowParameterDialog()
		{
			ParameterForm paramForm = new ParameterForm(paremeterDefinitionList, arguments);
			
			paramForm.ShowDialog();
		}


		/// <summary>
		/// Show CmdLine Help
		/// </summary>
		public void DisplayCommandLineHelp()
		{
			String exeName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
			int maxLength = 0;
			Console.WriteLine ("Usage: ");
			Console.Write(exeName + " ");
			foreach (ParameterDefinition def in paremeterDefinitionList)
			{
				if (!def.Required)
					Console.Write("[");
				if (def.ParameterType == ParameterType.Bool)
					Console.Write(def.Name + "=yes|no");
				else
					Console.Write(def.Name + "=..."); 
				if (!def.Required)
					Console.Write("]");
				Console.Write(" ");
				maxLength = Math.Max(maxLength, def.Name.Length);
			}
			Console.WriteLine("");
			Console.WriteLine("");

			foreach (ParameterDefinition def in paremeterDefinitionList)
			{
				Console.Write("  " + def.Name);
				for (int i = def.Name.Length; i <= maxLength; i++) Console.Write(" ");
				Console.WriteLine(def.Description);
			}
			Console.WriteLine("");
		}

		/// <summary>
		/// Add a new CommandLine Definition
		/// </summary>
		/// <param name="name"></param>
		/// <param name="description"></param>
		/// <param name="required"></param>
		public void AddParameterDefinition(string name, ParameterType type, string description, bool required)
		{
			AddParameterDefinition(name, type, description, required, null, null);
		}


		/// <summary>
		/// Add a new CommandLine Definition
		/// </summary>
		/// <param name="name"></param>
		/// <param name="description"></param>
		/// <param name="required"></param>
		/// <param name="default"></param>
		public void AddParameterDefinition(string name, ParameterType type, string description, bool required, string @default, string[] options)
		{
			ParameterDefinition def = new ParameterDefinition();
			def.Name = name;
			def.Description = description;
			def.Required = required;
			def.Default = @default;
			def.ParameterType = type;
			def.Options = options;
			paremeterDefinitionList.Add(def);
		}


		/// <summary>
		/// Load model
		/// </summary>
		public void LoadModel()
		{
			IMetaModelImport import = ImportFactory.GetImport(arguments["import"]);			
			using (StreamReader reader = new StreamReader(arguments["xmi"]))
			{
				model = import.Import(reader);
			}
		}

		/// <summary>
		/// Transform model
		/// </summary>
		public void Transform()
		{
			XmlReader ruleXmlReader = new XmlTextReader(Assembly.GetCallingAssembly().GetManifestResourceStream("transformations.xml"));
			Madason.MetaModel.Transformation.Transformer transformer = new Madason.MetaModel.Transformation.Transformer(ruleXmlReader);
			transformer.Transform(model);
		}


		//
		// Validate model
		//
		public void Validate()
		{
			try
			{
				ValidationIssueList issueList = new ValidationIssueList();
				model.ValidateRecursive(issueList);
				foreach (ValidationIssue issue in issueList)
				{
					Console.Out.WriteLine(issue.ToString());
				}
				if (issueList.ContainsErrors()) throw new ValidationException("Validation failed");
			}
			catch (ValidationException ve)
			{
				Console.WriteLine("validation failed");
				System.Diagnostics.Process.GetCurrentProcess().Kill();
			}
		}

		public bool GetBoolParam(string key)
		{
			if (arguments.ContainsKey(key))
			{
				return arguments[key].ToLower() == "yes";
			}
			return false;
		}
	}

	public struct ParameterDefinition
	{
		public string Name;
		public string Description;
		public bool Required;
		public string Default;
		public ParameterType ParameterType;
		public string[] Options;
	}

	public enum ParameterType
	{
		Text,
		Bool,
		File,
		Directory,
		Selection
	}

}
